﻿#pragma once

#include <xy/geom/manifold.h>

namespace xy
{

namespace geom
{

template <std::size_t D, std::size_t N, bool Rat = false> class bezier : public manifold<D, N>
{
    using vecd_t = vec<D, double>;       // 参数域向量
    using vecr_t = vec<N + Rat, double>; // (wP,w)
    using vec_t = vec<N, double>;        // P

  protected:
    math::ndarray<D, vecr_t> m_cpts;      // 控制点集
    std::array<std::size_t, D> m_degrees; // 每个维度上的次数

  public:
    /**
     * @brief 构造函数
     *
     * @param[in] cpts
     * @param[in] knots
     * @param[in] degrees
     * @param[in] normalize
     */
    bezier(math::ndarray<D, vecr_t> &&cpts, std::array<std::size_t, D> &&degrees, bool normalize)
        : m_cpts(std::move(cpts)), m_degrees(std::move(degrees))
    {
        m_cpts.fit_shape(pole_nums());
        if (m_cpts.size() != this->total_pole_num())
            throw std::logic_error("Control points not match.");

        // 如果为有理 bezier 曲线，则对点的坐标进行规范化 (P,w) -> (wP,w)
        if constexpr (Rat)
        {
            if (normalize)
            {
                for (auto &p : m_cpts.flatten())
                {
                    double w = p[N];
                    p[N] = 1.0;
                    p *= w;
                }
            }
        }
    }

    /**
     * @brief 获得每个维度上的控制点数
     *
     * @return std::array<std::size_t, D>
     */
    std::array<std::size_t, D> pole_nums() const
    {
        std::array<std::size_t, D> res;
        for (std::size_t i = 0; i < D; i++)
            res[i] = m_degrees[i] + 1;
        return res;
    }

    /**
     * @brief 获得 i 维度上的控制点数
     *
     * @param[in] i
     * @return std::size_t
     */
    std::size_t pole_num(std::size_t i = 0) const
    {
        return m_degrees[i] + 1;
    }

    /**
     * @brief 获得总的控制点数
     *
     * @return std::size_t
     */
    std::size_t total_pole_num() const
    {
        auto nums = pole_nums();
        std::size_t res = 1;
        for (auto num : nums)
            res *= num;
        return res;
    }

    /**
     * @brief 获得控制点
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(math::DimParams<D, Ts...>)
    vec_t pole(const Ts &...ts) const
    {
        if constexpr (Rat)
            return homogeneous_to_cartesian(m_cpts(ts...));
        else
            return m_cpts(ts...);
    }

    /**
     * @brief 设置控制点
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(math::DimParams<D, Ts...>)
    void set_pole(const vec_t &p, const Ts &...ts)
    {
        if constexpr (Rat)
            m_cpts(ts...) = cartesian_to_homogeneous(p) * weight(ts...);
        else
            m_cpts(ts...) = p;
    }

    /**
     * @brief 获得权重
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(math::DimParams<D, Ts...>)
    double weight(const Ts &...ts) const
    {
        if constexpr (Rat)
            return m_cpts(ts...)[N];
        else
            return 1.0;
    }

    /**
     * @brief 设置权重
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(math::DimParams<D, Ts...>)
    void set_weight(double w, const Ts &...ts)
    {
        static_assert(Rat, "Rational curve required.");
        m_cpts(ts...) *= w / m_cpts(ts...)[N];
    }

    /**
     * @brief 获得齐次控制点
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(math::DimParams<D, Ts...>)
    vecr_t inf_pole(const Ts &...ts) const
    {
        static_assert(Rat, "Rational curve required.");
        return m_cpts(ts...);
    }

    /**
     * @brief 设置齐次控制点
     *
     * @tparam Ts
     */
    template <class... Ts>
        requires(sizeof...(Ts) == D && (std::is_integral_v<Ts> && ...))
    void set_inf_pole(const vecr_t &p, const Ts &...ts)
    {
        static_assert(Rat, "Rational curve required.");
        m_cpts(ts...) = p;
    }

    /**
     * @brief 获得控制点集
     *
     * @return decltype(m_cpts)
     */
    auto poles() const -> const auto &
    {
        return m_cpts;
    }

    /**
     * @brief 移动控制点集
     *
     * @return decltype(m_cpts)
     */
    auto move_poles() -> decltype(m_cpts)
    {
        return std::move(m_cpts);
    }

    /**
     * @brief 设置控制点集
     *
     * @param[in] cpts
     */
    void set_poles(std::vector<vecr_t> &&cpts)
    {
        m_cpts = math::ndarray<D, vecr_t>{{std::move(cpts)}};
        m_cpts.fit_shape(pole_nums());
    }

    /**
     * @brief 获得 i 维度上的次数
     *
     * @param[in] i
     * @return std::size_t
     */
    std::size_t degree(std::size_t i = 0) const
    {
        return m_degrees[i];
    }

    /**
     * @brief 设置 i 维度上的次数
     *
     * @param[in] i
     * @return std::size_t&
     */
    std::size_t &degree(std::size_t i = 0)
    {
        return m_degrees[i];
    }

    /**
     * @brief 获得每个维度上的次数
     *
     * @return std::array<std::size_t, D>
     */
    const std::array<std::size_t, D> &degrees() const
    {
        return m_degrees;
    }

    /**
     * @brief 获得参数范围
     *
     * @param[in] i
     * @return std::array<double, 2>
     */
    std::array<double, 2> bound(std::size_t i = 0) const override
    {
        return {0, 1};
    }

    /**
     * @brief 是否是有理
     *
     * @return true
     * @return false
     */
    constexpr bool is_rational() const
    {
        return Rat;
    }
};

} // namespace geom

} // namespace xy